// project created on 11/23/2009 at 10:00 PM
// Compile:     c++ main.cpp -o FinalDB
// Run:         ./FinalDB

#include <iostream>
#include <fstream>
#include <vector>
#include <map>
#include <iomanip>			// setprecision(2)
#include <time.h>			// clock()
#include <stdlib.h> 		// atoi, atof
using namespace std;

struct Result {
    int totalcostcount;
    double totalcostsum;
};
struct Part{
	int partkey;
	int suppkey;
	double totalcost;	
};
struct Item{
	int orderkey;
	double totalcost;	
};

class Parts {
public:	
    void load(ifstream &file) {
	    std::fstream out("outputParts.dat", std::ios::out | std::ios::binary);
	    string key, aux1, aux2;
       	Part part;  
       	while (!getline(file, aux1, '|').eof()) { // partkey
       		getline(file, aux2,'|'); 			// suppkey
       		part.partkey = atoi(aux1.c_str());           
            part.suppkey = atof(aux2.c_str());
            getline(file, aux1, '|');
            getline(file, aux2, '|');
            part.totalcost = atof(aux1.c_str()) * atof(aux2.c_str());
            getline(file, aux1);					// read until newline
           	out.write(reinterpret_cast<char *>(&part), sizeof(Part));   
        }
        out.clear();
        out.close();        
    }
};

class LineItems {
public:
    void load(ifstream &file) {
        string aux1, aux2;
        int partkey, suppkey;
        fstream out("outputItems.dat", ios::out | ios::binary);
        fstream fileParts("outputParts.dat", ios::in | ios::binary);
        
        // get total number of records
        fileParts.seekg(0,ios::end);
        int records = fileParts.tellg()/sizeof(Part);
        fileParts.seekg(0,ios::beg);
       
        Item item;
        while (!getline(file, aux1, '|').eof()) {
            item.orderkey = atoi(aux1.c_str());
            getline(file, aux1,'|'); 		// partkey
            getline(file, aux2,'|'); 		// suppkey
            partkey = atoi(aux1.c_str());
            suppkey = atoi(aux2.c_str());
            vector<double> v = getParts(fileParts, records, partkey, suppkey);
            for (int i=0; i<v.size();i++) {
                item.totalcost = v[i];
            	out.write(reinterpret_cast<char *>(&item), sizeof(Item));
             }
            getline(file, aux1); // move to newline            
         }
        out.clear();
        out.close();
        fileParts.close();
        
    }
    // binary search
    vector<double> getParts(fstream &file, int totalRecords, int partkey, int suppkey){
	    int recordSize = sizeof(Part);
	    vector<double> result;
	    Part part;
	    file.clear(); // reset file.eof bit
	    file.seekg(0,ios::beg);
	    int record = binarySearch(file,partkey,0,totalRecords);
	 
	    if(record > -1){ // found and not bof
	    	int position;
	    	if(record>0){
		    	// read back becouse duplicate records
		    	record--;
		    	position = record * recordSize;
			    file.seekg(position, ios::beg);
		    	file.read(reinterpret_cast<char *>(&part), recordSize);
		    	file.seekg(position,ios::beg);	
		    	while(part.partkey == partkey){
			    	record--;
			    	if(record < 0) break;
			    	position = record * recordSize;
			    	file.seekg(position, ios::beg);
		    		file.read(reinterpret_cast<char *>(&part), recordSize);
		    		file.seekg(position, ios::beg);	    			    			    		
		    	}	   
		    	record++;
		    }
		    position = record * recordSize;
		    file.seekg(position, ios::beg);
	    	file.read(reinterpret_cast<char *>(&part), recordSize);	    	
	    	while(part.partkey == partkey && !file.eof()){
		    	if(part.suppkey == suppkey)
		    		result.push_back(part.totalcost);
		    	file.read(reinterpret_cast<char *>(&part), recordSize);
	    	}		
	    }
		return result;
    }   
    int binarySearch(fstream &file, int value, int low, int high) {
   		if (high < low)
       		return -1; // not found
   		int record = low + ((high - low) / 2);
   		Part part;
   		int position = record * sizeof(Part);
   		file.seekg(position, ios::beg);
		file.read(reinterpret_cast<char *>(&part), sizeof(Part));	
		file.seekg(position,ios::beg);		 
   		if (part.partkey > value)
       		return binarySearch(file, value, low, record-1);
   		else if (part.partkey < value)
       		return binarySearch(file, value, record+1, high);
   		else
       		return record; // found
	}
};

class Orders {
public:
	
    map<string,Result> loadAndGetResult(ifstream &file) {
	    map<string,Result> results;
        int orderkey;
        string date, aux;
        Result r;
        fstream fileItems("outputItems.dat", ios::in | ios::binary);
        
        // get total number of records
        fileItems.seekg(0,ios::end);
        int records = fileItems.tellg()/sizeof(Item);
        fileItems.seekg(0,ios::beg);
        
        while (!getline(file, aux, '|').eof()) {
            orderkey = atoi(aux.c_str());
            getline(file, aux,'|'); 	// custkey
            getline(file, aux,'|'); 	// orderstatus
            getline(file, aux,'|'); 	//	totalprice
            getline(file, date,'|');
            getline(file, aux); 		// fix this, I need to move to newline
            r = results[date];
            if (results.find(date) == results.end()) {
                r.totalcostcount = 0;
                r.totalcostsum = 0;
            }
            vector<double> v = getItems(fileItems, records, orderkey);
            for (int j = 0; j < v.size(); ++j) {
                r.totalcostcount ++;
                r.totalcostsum += v[j];
            }
            results[date] = r;
        }
        fileItems.close();
        return results;
    }
    // binary search
    vector<double> getItems(fstream &file, int totalRecords, int key){
	   	// cout << key <<"\t";
		int recordSize = sizeof(Item);		
	    vector<double> result;
	    Item item;
	    file.clear(); // reset file.eof bit
	    file.seekg(0,ios::beg);
	    int record = binarySearch(file,key,0,totalRecords);
	     
	    if(record > -1){ // found and not bof
	    	int position;
	    	if(record > 0){
		    	// read back becouse duplicate records
		    	record--;
		    	position = record * recordSize;
			    file.seekg(position, ios::beg);
		    	file.read(reinterpret_cast<char *>(&item), recordSize);
		    	file.seekg(position,ios::beg);	
		    	
		    	while(item.orderkey == key){
			    	record--;
			    	if(record < 0) break;
			    	position = record * recordSize;
			    	file.seekg(position, ios::beg);
		    		file.read(reinterpret_cast<char *>(&item), recordSize);
		    		file.seekg(position,ios::beg);	    			    			    		
		    	}	
		    	record++;
		    	
	    	}
		    position = record * recordSize;
		    file.seekg(position, ios::beg);
	    	file.read(reinterpret_cast<char *>(&item), recordSize);	    	
	    	while(item.orderkey == key && !file.eof()){
		    	result.push_back(item.totalcost);
		    	file.read(reinterpret_cast<char *>(&item), recordSize);
	    	}		
	    }
		return result;
    }   
    int binarySearch(fstream &file, int value, int low, int high) {
	    if (high < low)
       		return -1; // not found
   		int record = low + ((high - low) / 2);
   		Part part;
   		int position = record * sizeof(Item);
   		file.seekg(position, ios::beg);
		file.read(reinterpret_cast<char *>(&part), sizeof(Item));	
		file.seekg(position,ios::beg);		 
   		if (part.partkey > value)
       		return binarySearch(file, value, low, record-1);
   		else if (part.partkey < value)
       		return binarySearch(file, value, record+1, high);
   		else
       		return record; // found
	}
};
/*
void show_mem_usage() {
    using std::ios_base;

    double vm_usage     = 0.0;
    ifstream stat_stream("/proc/self/stat",ios_base::in);
    string pid, comm, state, ppid, pgrp, session, tty_nr;
    string tpgid, flags, minflt, cminflt, majflt, cmajflt;
    string utime, stime, cutime, cstime, priority, nice;
    string O, itrealvalue, starttime;

    unsigned long vsize;
    long rss;
    stat_stream >> pid >> comm >> state >> ppid >> pgrp >> session >> tty_nr
    >> tpgid >> flags >> minflt >> cminflt >> majflt >> cmajflt
    >> utime >> stime >> cutime >> cstime >> priority >> nice
    >> O >> itrealvalue >> starttime >> vsize >> rss; // don't care about the rest

    long page_size_kb = sysconf(_SC_PAGE_SIZE) / 1024; // in case x86-64 is configured to use 2MB pages
    vm_usage     = vsize / 1024.0;
    cout << "Memory usage (KB): " << vm_usage << endl;
}
*/
int main (int argc, char *argv[]) {
    // sum amount records with some key
    clock_t start, finish;
    start = clock();
    ifstream file1("partsupp.tbl") ;
    ifstream file2("lineitem.tbl") ;
    ifstream file3("orders.tbl") ;

    //show_mem_usage();

    if (!file1 || !file2 || !file3)
        cout << "Could not open some file" << endl ;

    cout <<"Reading partsup.tbl..."<<endl;
    Parts parts;
    parts.load(file1);
	file1.close();
    //show_mem_usage();

    cout <<"Reading lineitem.tbl..."<<endl;
    LineItems items;
    items.load(file2);
	file2.close();
    //show_mem_usage();

    cout <<"Reading orders.tbl..."<<endl;
    Orders orders;
    map<string,Result> results = orders.loadAndGetResult(file3);
    //show_mem_usage();
    file3.close();

    //cout <<"Total records: "<< results.size() <<endl;

    for (map<string,Result>::iterator i = results.begin(); i != results.end(); i++)
        cout << i->first << ": "
        << setw(14) << setiosflags(ios::fixed) /*<< setprecision(4)*/
        <<  i->second.totalcostsum / double(i->second.totalcostcount) << endl;
    cout << "Total lines: " << results.size() << endl;

    //show_mem_usage();

    finish = clock();
    cout << "Time: "<< (double(finish - start)/CLOCKS_PER_SEC) << endl;

    cout << "Ok"<< endl;
    //getchar();
    return 0;
}
